package fr.lip6.meta.ple.generation.aspectj;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.eclipse.gmt.modisco.java.AbstractTypeDeclaration;
import org.eclipse.gmt.modisco.java.BodyDeclaration;
import org.eclipse.gmt.modisco.java.ClassDeclaration;
import org.eclipse.gmt.modisco.java.MethodDeclaration;
import org.eclipse.gmt.modisco.java.emf.impl.ConstructorDeclarationImpl;
import org.eclipse.gmt.modisco.java.emf.impl.FieldDeclarationImpl;
import org.eclipse.gmt.modisco.java.emf.impl.MethodDeclarationImpl;
import org.eclipse.gmt.modisco.java.emf.impl.PackageImpl;

import artefact.umlClassDiagram.CreateAssociation;
import artefact.umlClassDiagram.CreateClass;
import artefact.umlClassDiagram.CreateGeneralization;
import artefact.umlClassDiagram.CreateOperation;
import artefact.umlClassDiagram.CreateOperationRefinement;
import artefact.umlClassDiagram.CreatePackage;
import artefact.umlClassDiagram.impl.CreateAttributeImpl;
import fr.lip6.meta.ple.featureIdentification.Feature;
import fr.lip6.meta.ple.generation.strategy.ReadXmiStrategy;
import fr.lip6.meta.ple.generation.tools.PLConstructionFactory;
import fr.lip6.meta.tools.StatementPrimitive;
import fr.lip6.meta.tools.Trigger;

public class PLConstructionFactoryAJ extends PLConstructionFactory {
	private String srcDir;
	static HashMap<String, ArrayList<Trigger>> packageBaseClasses = new HashMap<String, ArrayList<Trigger>>();
	HashMap<Feature, ArrayList<String>> featuresAspects = new HashMap<Feature, ArrayList<String>>();
	private ReadXmiStrategy readXmi = null;

	public PLConstructionFactoryAJ(String srcDir) {
		this.srcDir = srcDir;
	}
	
	@Override
	public PrintStream createPackage(	StatementPrimitive stPrimitive,
										Feature feat,
										ArrayList<String> featClasses	) throws FileNotFoundException {
		CreatePackage createPackage				= (CreatePackage) stPrimitive.getPrimitive();
		String pckgname							= createPackage.getName();
		String path								= createPackage.getLien();
		PackageImpl pckg						= GenerateFromXmiAJ.readpackage(path, pckgname);
		Iterator<AbstractTypeDeclaration> list	= pckg.getOwnedElements().iterator();
		Iterator<ClassDeclaration> featClassDeclIt;
		PrintStream out = null;

		while(list.hasNext()) {
			pckg.getOwnedElements().remove(0);
		}
		
		featClassDeclIt = GenerateFromXmiAJ.getClassDeclarations(feat, createPackage.getName()).iterator();
		
		while(featClassDeclIt.hasNext()) {
			ClassDeclaration featClassDecl = featClassDeclIt.next();
			File dotDir;
			File dotFile;
			FileOutputStream fout;
			StringBuilder stringBuilder = new StringBuilder();
			String pckgPath;
			//String featname;
			
			//System.out.println("PLConstructionFactory.createPackage(): class: "+featClassDecl.getName());
			
			pckgPath = srcDir+"/"+pckgname;
				
			dotDir = new File(pckgPath);
			
			if (!dotDir.exists()) {
				dotDir.mkdir();
			}
			
			dotFile = new File(pckgPath+"/"+featClassDecl.getName()+".java");
			
			fout = new FileOutputStream(dotFile);
			out = new PrintStream(fout);
			
			setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
			
			stringBuilder.append("package "+featClassDecl.getPackage().getName()+";\n\n");
			
			for(String imppackage : GenerateFromXmiAJ.importedpackage) {
				stringBuilder.append(imppackage+".*;\n");
			}
			
			stringBuilder.append("\n");
			
			stringBuilder.append(((ReadXmi) readXmi).getClassDeclarationAndBodyText(featClassDecl, false));
			stringBuilder.append("\n");
			
			System.err.println(stringBuilder.toString());
			out.print(stringBuilder.toString());
			
			packageBaseClasses.put(featClassDecl.getName(), feat);
			
			featClasses.add(featClassDecl.getName());
		}
		
		return out;
	}
	
	@Override
	public PrintStream createClass(	CreateClass crClass,
									Feature feat,
									ArrayList<String> classesName	) throws FileNotFoundException {
		PrintStream out = null;
		
		//System.out.println("PLConstructionFactory.createClass(): class: "+crClass.getName());
		
		String pckg = crClass.getOwener();
		ClassDeclaration classDecl = GenerateFromXmiAJ.getClassDeclaration(crClass.getLien(), pckg, crClass.getName());
		String pckgPath;
		
		Iterator<BodyDeclaration> listatt = classDecl.getBodyDeclarations().iterator();
		while(listatt.hasNext()) {
			classDecl.getBodyDeclarations().remove(0);
		}
		Iterator<FieldDeclarationImpl> fds = GenerateFromXmiAJ.getFieldsDeclaration(feat, pckg, crClass.getName()).iterator();
		
		while(fds.hasNext()) {
			FieldDeclarationImpl fd = fds.next();
			classDecl.getBodyDeclarations().add(fd);
		}
		
		Iterator<MethodDeclaration> ops = GenerateFromXmiAJ.getOperations(feat, pckg, crClass.getName()).iterator();
		
		while(ops.hasNext()) {
			MethodDeclarationImpl fd1 = (MethodDeclarationImpl) ops.next();
			
			classDecl.getBodyDeclarations().add(fd1);
		}
		
		pckgPath = srcDir+"/"+pckg;
		
		File dotDir = new File(pckgPath);
		
		if (!dotDir.exists()) {
			dotDir.mkdir();
		}
		
		File dotFile = new File(pckgPath+"/"+crClass.getName()+".java");
		FileOutputStream fout;
		
		fout = new FileOutputStream(dotFile);
		out = new PrintStream(fout);
		StringBuilder stringBuilder = new StringBuilder();
		
		setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
		
		stringBuilder.append("package "+classDecl.getPackage().getName()+";\n\n");
		
		for(String imppackage : GenerateFromXmiAJ.importedpackage) {
			stringBuilder.append(imppackage+".*;\n");	
		}
		
		stringBuilder.append("\n");
		
		if (packageBaseClasses.containsKey(classDecl.getName())) {
			// refinement
			stringBuilder.append(((ReadXmi) readXmi).getClassDeclarationAndBodyText(classDecl, true));
		} else {
			stringBuilder.append(((ReadXmi) readXmi).getClassDeclarationAndBodyText(classDecl, false));
		}
		
		stringBuilder.append("\n");
		
		System.err.println(stringBuilder.toString());
		out.println(stringBuilder.toString());
		
		return out;
	}
	
	@Override
	public PrintStream createGeneralization(	CreateGeneralization crGen,
												Feature feat, ArrayList<String> classesName	) throws FileNotFoundException {
		PrintStream out = null;
		
		//System.out.println(	"PLConstructionFactory.createGeneralization(): "+
		//					"sub: "+crGen.getSub()+" "+
		//					"super: "+crGen.getSuper());
		
		String [] lien = crGen.getLien().split("::");
		String pckg = lien[1];
		String pckgPath;
		StringBuilder stringBuilder = new StringBuilder();
		
		ClassDeclaration classDecl = GenerateFromXmiAJ.getClassDeclaration(lien[0], pckg, crGen.getSub());
		
		Iterator<BodyDeclaration> listatt = classDecl.getBodyDeclarations().iterator();
		while(listatt.hasNext()) {
			classDecl.getBodyDeclarations().remove(0);
		}
		Iterator<FieldDeclarationImpl> fieldDecls = GenerateFromXmiAJ.getFieldsDeclaration(feat, pckg, crGen.getSub()).iterator();
		
		while(fieldDecls.hasNext()) {
			FieldDeclarationImpl fieldDecl = fieldDecls.next();
			
			classDecl.getBodyDeclarations().add(fieldDecl);
		}
		
		Iterator<MethodDeclaration> methodDecls = GenerateFromXmiAJ.getOperations(feat, pckg, crGen.getSub()).iterator();
		
		while(methodDecls.hasNext()) {
			MethodDeclarationImpl methodDecl = (MethodDeclarationImpl) methodDecls.next();
			
			classDecl.getBodyDeclarations().add(methodDecl);
		}
		Iterator<MethodDeclaration> opref = GenerateFromXmiAJ.getRefinementoperations(feat, pckg, crGen.getSub()).iterator();
		
		while(opref.hasNext()) {
			MethodDeclarationImpl fd1 = (MethodDeclarationImpl) opref.next();
			
			classDecl.getBodyDeclarations().add(fd1);
		}
		
		pckgPath = srcDir+"/"+pckg;
		
		File dotDir = new File(pckgPath);
		
		if (!dotDir.exists()) {
			dotDir.mkdir();
		}
		
		File dotFile = new File(pckgPath+"/"+crGen.getSub()+".java");
		FileOutputStream fout;
		
		fout = new FileOutputStream(dotFile);
		out = new PrintStream(fout);
		
		setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
		
		stringBuilder.append("package "+pckg+";\n\n");
		
		for(String imppackage: GenerateFromXmiAJ.importedpackage) {
			stringBuilder.append(imppackage+".*;\n");
		}
		
		stringBuilder.append("\n");
		
		if (packageBaseClasses.containsKey(classDecl.getName())) {
			stringBuilder.append(((ReadXmi) readXmi).getClassDeclarationAndBodyText(classDecl, true));
		} else {
			stringBuilder.append(((ReadXmi) readXmi).getClassDeclarationAndBodyText(classDecl, false));	
		}
		
		stringBuilder.append("\n");
		
		out.println(stringBuilder.toString());
		
		return out;
	}
	
	@Override
	public PrintStream createAttribute(	CreateAttributeImpl crAttr,
										Feature feat,
										ArrayList<String> featClasses	) throws FileNotFoundException {
		
		//System.out.println("PLConstructionFactory.createAttribute(): class: "+crAttr.getOwner());
		
		featClasses.add(crAttr.getOwner());
		
		boolean isAspect = packageBaseClasses.containsKey(crAttr.getOwner());
		PrintStream out = null;
		String lien[] = crAttr.getLien().split("::"); // .xmi path, package
		//String xmiPath = lien[0];
		String pckg = lien[1];
		StringBuilder stringBuilder = new StringBuilder();
		File dotDir;
		File dotFile;
		FileOutputStream fout;
		String dirPath = srcDir+"/"+pckg;
		String filename;
		
		if (isAspect) {
			String aspect = pckg.replace('.', '_')+"_"+crAttr.getOwner()+feat.getId();
			filename = crAttr.getOwner()+feat.getId()+".aj";
			
			if (featuresAspects.containsKey(feat)) {
				featuresAspects.get(feat).add(aspect);
			} else {
				ArrayList<String> aspects = new ArrayList<String>();
				
				aspects.add(aspect);
				featuresAspects.put(feat, aspects);
			}
		} else {
			filename = crAttr.getOwner()+".java";
		}
		
		dotDir = new File(dirPath);
		
		if (!dotDir.exists()) {
			dotDir.mkdir();
		}
		
		dotFile = new File(dirPath+"/"+filename);
		fout = new FileOutputStream(dotFile);
		out = new PrintStream(fout);
		
		setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
		
		stringBuilder.append("package "+pckg+";\n\n");
		
		for(String imppackage: GenerateFromXmiAJ.importedpackage) {
			stringBuilder.append(imppackage+".*;\n");
		}
		
		stringBuilder.append("\n");
		
		stringBuilder.append((isAspect ? "aspect "+crAttr.getOwner()+feat.getId() : "public class "+crAttr.getOwner())+" {\n");
		
		Iterator<FieldDeclarationImpl> fields = GenerateFromXmiAJ.getFieldsDeclaration(feat, pckg, crAttr.getOwner()).iterator();
		
		while(fields.hasNext()) {
			FieldDeclarationImpl fld = fields.next();
			HashMap<String, String> fdText = ReadXmiAJ.getFieldDeclarationTextList(/*fieldDecl*/fld);
			
			stringBuilder.append(
					"\t"
				// AspectJ does not provide protected inter-type members.
				+	(	isAspect ? (fdText.get("visibility").equals("protected") ? "private" : fdText.get("visibility"))
						: fdText.get("visibility")	) + " "
				+	fdText.get("type") + " "
				+	(isAspect ? crAttr.getOwner()+"." : "") + fdText.get("name") + ";"
				+	"\n");
		}
		
		stringBuilder.append("\n");
		
		Iterator<MethodDeclaration> methodDecls = GenerateFromXmiAJ.getOperations(feat, pckg, crAttr.getOwner()).iterator();
		
		while(methodDecls.hasNext()) {
			MethodDeclarationImpl methodDecl = (MethodDeclarationImpl) methodDecls.next();
			HashMap<String, String> mdbText = ((ReadXmiAJ) readXmi).getMethodDeclarationAndBodyTextList(methodDecl);
			
			stringBuilder.append(
					"\t"
				+	(	isAspect ? (mdbText.get("visibility").equals("protected") ? "private" : mdbText.get("visibility"))
						: mdbText.get("visibility")	) + " "
				+	mdbText.get("type") + " "
				+	(isAspect ? crAttr.getOwner()+"." : "") + mdbText.get("name") + "("
				+	mdbText.get("parameters") + ")"
				+	mdbText.get("body")
				+	"\n");
		}
		
		stringBuilder.append("}\n");
		
		System.err.println(stringBuilder.toString());
		out.print(stringBuilder.toString());
		//out.close();
		
		return out;
	}
	
	@Override
	public PrintStream createOperation(	CreateOperation crOp,
										Feature feat,
										ArrayList<String> featClasses	) throws FileNotFoundException {
		PrintStream out = null;
		
		//System.out.println("PLConstructionFactory.createOperation(): class: "+crOp.getOwener());
		
		featClasses.add(crOp.getOwener());
		
		boolean isAspect = packageBaseClasses.containsKey(crOp.getOwener());
		String lien[] = crOp.getLien().split("::");
		//String xmiPath = lien[0];
		String pckg = lien[1];
		StringBuilder stringBuilder = new StringBuilder();
		File dotDir;
		File dotFile;
		FileOutputStream fout;
		String dirPath;
		String filename;
		String featname;

		if (feat.getId().split("_").length > 1) {
			featname = feat.getName();
		} else {
			featname = feat.getId();
		}
		
		dirPath = srcDir+"/"+pckg;
		
		if (isAspect) {
			String aspect = pckg.replace('.', '_')+"_"+crOp.getOwener()+featname;
			
			filename = crOp.getOwener()+featname+".aj";
			
			if (featuresAspects.containsKey(feat)) {
				featuresAspects.get(feat).add(aspect);
			} else {
				ArrayList<String> aspects = new ArrayList<String>();
				
				aspects.add(aspect);
				featuresAspects.put(feat, aspects);
			}
		} else {
			filename = crOp.getOwener()+".java";
		}
		
		dotDir = new File(dirPath);
		
		if (!dotDir.exists()) {
			dotDir.mkdir();
		}
		
		dotFile = new File(dirPath+"/"+filename);
		fout = new FileOutputStream(dotFile);
		out = new PrintStream(fout);
		
		setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
		
		stringBuilder.append("package "+pckg+";\n\n");
		
		for(String imppackage : GenerateFromXmiAJ.importedpackage) {
			stringBuilder.append(imppackage+".*;\n");
		}
		
		stringBuilder.append("\n");
		
		stringBuilder.append((isAspect ? "aspect "+crOp.getOwener()+featname : "public class "+crOp.getOwener())+" {\n");
		
		//FieldDeclarationImpl fieldDecl = GenerateFromXMI.getFieldDeclaration(lien[0], pckg, crOp.getOwener(), crOp.getName());
		Iterator<FieldDeclarationImpl> fields = GenerateFromXmiAJ.getFieldsDeclaration(feat, pckg, crOp.getOwener()).iterator();
		
		while(fields.hasNext()) {
			FieldDeclarationImpl fld = fields.next();
			HashMap<String, String> fdText = ReadXmiAJ.getFieldDeclarationTextList(/*fieldDecl*/fld);
			
			stringBuilder.append(
					"\t"
				// AspectJ does not provide protected inter-type members.
				+	(	isAspect ? (fdText.get("visibility").equals("protected") ? "private" : fdText.get("visibility"))
						: fdText.get("visibility")	) + " "
				+	fdText.get("type") + " "
				+	(isAspect ? crOp.getOwener()+"." : "") + fdText.get("name") + ";"
				+	"\n");
		}
		
		stringBuilder.append("\n");
		
		Iterator<ConstructorDeclarationImpl> consDecls = GenerateFromXmiAJ.getConstructorsDeclaration(feat, pckg, crOp.getName()).iterator();
		
		while(consDecls.hasNext()) {
			ConstructorDeclarationImpl consDecl = consDecls.next();
			HashMap<String, String> mdbText = ((ReadXmiAJ) readXmi).getConstructorDeclarationAndBodyTextList(consDecl);
			
			stringBuilder.append(
					"\t"
				+	(	isAspect ? (mdbText.get("visibility").equals("protected") ? "private" : mdbText.get("visibility"))
						: mdbText.get("visibility")	) + " "
				+	(isAspect ? crOp.getOwener()+"." : "") + "new("
				+	mdbText.get("parameters") + ")"
				+	mdbText.get("body")
				+	"\n");
		}
		
		Iterator<MethodDeclaration> methodDecls = GenerateFromXmiAJ.getOperations(feat, pckg, crOp.getOwener()).iterator();
		
		while(methodDecls.hasNext()) {
			MethodDeclaration methodDecl = methodDecls.next();
			HashMap<String, String> mdbText = ((ReadXmiAJ) readXmi).getMethodDeclarationAndBodyTextList(methodDecl);
			
			stringBuilder.append(
					"\t"
				+	(	isAspect ? (mdbText.get("visibility").equals("protected") ? "private" : mdbText.get("visibility"))
						: mdbText.get("visibility")	) + " "
				+	mdbText.get("type") + " "
				+	(isAspect ? crOp.getOwener()+"." : "") + mdbText.get("name") + "("
				+	mdbText.get("parameters") + ")"
				+	mdbText.get("body")
				+	"\n");
		}
		
		stringBuilder.append("}\n");
		
		System.err.println(stringBuilder.toString());
		out.print(stringBuilder.toString());
		//out.close();
		
		return out;
	}

	@Override
	public PrintStream createOperationRefinement(	CreateOperationRefinement crOpRef,
													Feature feat,
													ArrayList<String> featClasses	) throws FileNotFoundException {
		PrintStream out = null;
		
		//System.out.println(	"PLConstructionFactory.createOperationRefinement(): "+
		//					"owner: "+crOpRef.getOwner()+" "+
		//					"super: "+crOpRef.getSuper());
		
		featClasses.add(crOpRef.getOwner());
		String [] lien = crOpRef.getLien().split("::");
		StringBuilder stringBuilder = new StringBuilder();
		String dirPath = srcDir+"/"+lien[1];
		File dotDir = new File(dirPath);
		File dotFile = new File(dirPath+"/"+crOpRef.getOwner()+".aj");
		FileOutputStream fout;
		
		if (!dotDir.exists()) {
			dotDir.mkdir();
		}
		
		fout = new FileOutputStream(dotFile);
		out = new PrintStream(fout);
		
		setPrintFile(new PrintFile(stringBuilder, dotDir, dotFile, fout, out));
		
		for(String imppackage : GenerateFromXmiAJ.importedpackage) {
			stringBuilder.append(imppackage+".*;\n");
		}
		
		if (packageBaseClasses.containsKey(crOpRef.getOwner())) {
			stringBuilder.append("public refines class "+crOpRef.getOwner()+" {\n");
		} else {
			stringBuilder.append("public class "+crOpRef.getOwner()+" {\n");	
		}
		
		FieldDeclarationImpl fd = GenerateFromXmiAJ.getFieldDeclaration(lien[0], lien[1], crOpRef.getOwner(), crOpRef.getName());
		Iterator<FieldDeclarationImpl> fds = GenerateFromXmiAJ.getFieldsDeclaration(feat, lien[1],crOpRef.getOwner()).iterator();
		
		while(fds.hasNext()) {
			fds.next();
			
			stringBuilder.append(readXmi.getFieldDeclarationText(fd)+"\n");
		}
		
		Iterator<ConstructorDeclarationImpl> consDeclIt = GenerateFromXmiAJ.getConstructorsDeclaration(feat, lien[1], crOpRef.getName()).iterator();
		
		while(consDeclIt.hasNext()) {
			ConstructorDeclarationImpl consDecl = (ConstructorDeclarationImpl) consDeclIt.next();
			stringBuilder.append(((ReadXmi) readXmi).getConstructorDeclarationAndBodyText(consDecl)+"\n");
		}
		
		Iterator<MethodDeclaration> ms = GenerateFromXmiAJ.getOperations(feat, lien[1], crOpRef.getOwner()).iterator();
		
		while(ms.hasNext()) {
			MethodDeclaration m = ms.next();
			
			stringBuilder.append(((ReadXmi) readXmi).getMethodDeclarationAndBodyText(m)+"\n");
		}
		
		stringBuilder.append("}\n");
		
		System.err.println(stringBuilder.toString());
		out.print(stringBuilder.toString());
		//out.close();
		
		return out;
	}

	@Override
	public PrintStream createAssociation(	CreateAssociation crAsso,
											Feature feat,
											ArrayList<String> classesName	) throws FileNotFoundException {
		PrintStream out = null;
		
		//System.out.println("PLConstructionFactory.createAssociation(): source: "+crAsso.getSource());
		
		return out;
	}
	
	public String getSrcDir() {
		return srcDir;
	}

	public void setReadXmi(ReadXmiStrategy readXmi) {
		this.readXmi = readXmi;
	}
}